# copyrigth Rodrigo Garcia strysg@riseu.net
# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public Licence as published
# by the Free Software Foundation; either version 3 of the Licence,
# or (at your option) any later version.

# Shutter is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.

# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

import re
import collections

class automata_cadenas:
    '''
    Automata generico que reconoce cadenas como expresiones
    regulares

    Ejemplo de Creacion funcion para la instruccion `mostrar':
    M1 = automata_cadenas(dict_FTrans, estadosFinales)
    
    Donde:

     dict_FTrans= {
      0 : [("mostrar ",1)],
      1 : [("^[a-zA-Z][a-zA-Z0-9_]*",6), (r'^"',2)],
      2 : [('^\W\"', 4)],
      4 : [("\."),5],
      6 : [("\."),5],
     }
    
    En este diccionario estan las reglas que acepta cad estado
    la llave en el diccionario es el estado en que parte,
    [] es la lista de reglas, cada regla contiene una tupla
    () donde el primer elemento es la expresion regular que
    reconoce el automata y el segundo elemento es el estado
    al que se mueve al aceptar la expresion regular.

     estadosFinales = [5]

    Es la lista de estados finales del automata.
    '''
    K = []
    q0 = 0
    F = []
    estados_recorridos = []
    estado = 0
    evaluada = ''
    dict_FTrans = {}
    
    def __init__(self, dict_FTrans, estadosFinales):
        self.F = estadosFinales

        for f in self.F:
            self.K.append(f)

        for k in dict_FTrans.keys():
            self.K.append(k)
        self.q0 = 0
        self.estado = 0
        self.evaluada = ''
        self.dict_FTrans = dict_FTrans
    
    def FTrans(self, cadena): # evalua la funcion de transicion
        self.evaluada = cadena
        cad = cadena
        partidas = []
        reglas_cadena_llegadas = []

        # ordenando por clave (estado de partida)
        dict_FTrans_ordenado = collections.OrderedDict(sorted(self.dict_FTrans.items()))
        
        # haciendo el recorrido
        while (self.estado not in self.F) and (self.estado != -1):
            # se busca el estado correspondiente y se guarda sus reglas para 
            # estados siguientes
            regla_actual = dict_FTrans_ordenado[self.estado]
            
            # comprobando reglas de transicion de ese estado
            for regla in regla_actual:
                estado_siguiente = regla[1]
                
                er = regla[0] # expresion regular
                m = re.search(er, cad)
                            
                if m is not None:
                    self.estados_recorridos.append(self.estado)
                    # "consumiendo" cadena
                    cad = cad[len(m.group(0)):]
                    self.estado = estado_siguiente
                    break
                else:
                    self.estado = -1

        return self.estado

    def aceptado(self):
        ''' Devuelve True si la ultima expresion evaluada por
        este automata ha sido aceptada'''
        if self.estado in self.F:
            return True
        return False
    
class M_mostrar:
    '''
    Automata que reconoce:
    mostrar "texto cualquiera".
    mostrar variable.
    '''
    K = [0,1,2,3,4,5,6] # conjunto finito de estados
    q0 = 0              # estado inicial
    F = [5]             # estados finales
    Alfabeto = ""       # cadena
    estados_recorridos = []
    estado = 0
    evaluada = ''
    def FTrans(self, cadena):    # funcion de trancision
        ''' Funcion de trancision retorna un estado 
            Se sabe que el automata ha aceptado la cadena
            si y solo si la FTrans termina en un estado final'''
        self.evaluada = cadena
        cad = cadena
        while (self.estado not in self.F) and (self.estado != -1):
            if self.estado == self.q0:
                if cad.startswith("mostrar "):
                    self.estados_recorridos.append(0)
                    cad = cad[len("mostrar "):] 
                    self.estado = 1
                else:
                    self.estado = -1
            elif self.estado == 1:
                W = "^[a-zA-Z][a-zA-Z0-9_]*"
                m = re.search(W,cad)

                if cad.startswith('"'):
                    self.estados_recorridos.append(1)
                    cad = cad[1:]
                    self.estado = 2
                elif m is not None:
                    self.estados_recorridos.append(1)
                    cad = cad[len(m.group(0)):]
                    self.estado = 6
                else:
                    self.estado = -1
            elif self.estado == 2:
                if cad.find('"') != -1:
                    self.estados_recorridos.append(2)
                    cad = cad[cad.find('"')+1:]
                    #self.estado = 3
                    self.estados_recorridos.append(3)
                    self.estado = 4
                else:
                    self.estado = -1
            elif self.estado == 4 or self.estado == 6:
                self.estados_recorridos.append(self.estado)
                if cad == ".":
                    self.estado = 5
                    print (cadena[len("mostrar "):-1])
                else:
                    self.estado = -1

        return self.estado
        
    def aceptado(self):
        ''' Devuelve True si la ultima expresion evaluada por
        este automata ha sido aceptada'''
        if self.estado in self.F:
            return True
        return False

    def mostrar_errores(self):
        ''' Segun la ultima cadena evaluada muestra 
        en que estado del automata se ha generado el error'''
        ultimoEstadoAlcanzado = self.estados_recorridos[-1]

    def __init__(self):
        self.q0 = 0
        self.estados_recorridos = []
        self.evaluada = ''

class M_declarar_enteros:
    K = [0,1,2,3,4,5] # conjunto finito de estados
    q0 = 0              # estado inicial
    F = [5]             # estados finales
    Alfabeto = ""       # cadena
    estados_recorridos = []
    estado = 0
    evaluada = ''
    def FTrans(self, cadena):
        self.evaluada = cadena
        cad = cadena
        while (self.estado not in self.F) and (self.estado != -1):
            if self.estado == self.q0:
                if cad.startswith("entero "):
                    self.estados_recorridos.append(0)
                    cad = cad[len("entero "):]
                    self.estado = 1
                else:
                    self.estado = -1
            elif self.estado == 1:
                W = "^[a-zA-Z][a-zA-Z0-9_]*"
                m = re.search(W,cad)

                if m is not None:
                    self.estados_recorridos.append(1)
                    cad = cad[len(m.group(0)):]
                    self.estado = 2
                else:
                    self.estado = -1
            elif self.estado == 2:
                er = "(= )|( =)|( = )|="
                m = re.search(er,cad)
                
                if m is not None:
                    self.estados_recorridos.append(self.estado)
                    cad = cad[len(m.group(0)):]
                    self.estado = 3
                else:
                    self.estado = -1
            elif self.estado == 3:
                er = "[0-9]+"
                m = re.search(er,cad)

                if m is not None:
                    self.estados_recorridos.append(self.estado)
                    self.estado = 4
                    cad = cad[len(m.group(0)):]
                else:
                    self.estado = -1
            elif self.estado == 4:
                if cad == '.':
                    self.estados_recorridos.append(self.estado)
                    self.estado = 5
                    # estado final
                    print(cadena)
                else:
                    self.estado = -1

        return self.estado
        
    def aceptado(self):
        ''' Devuelve True si la ultima expresion evaluada por
        este automata ha sido aceptada'''
        if self.estado in self.F:
            return True
        return False

    def mostrar_errores(self):
        ''' Segun la ultima cadena evaluada muestra 
        en que estado del automata se ha generado el error'''
        ultimoEstadoAlcanzado = self.estados_recorridos[-1]
        
